namespace SimpleWebServices.Web.UI.Html.Elements
{
    using System;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Xml.XPath;
    using SimpleWebServices;
    using SimpleWebServices.Data;
    using SimpleWebServices.Globalization;
    using SimpleWebServices.Security.Cryptography;
    using SimpleWebServices.Web.UI.Html.Validation;
    using Xunit;

    public sealed class CustomElementFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<CustomElement<Element>>()
                .DerivesFrom<SimpleWebServices.Data.Record>()
                .IsAbstractBaseClass()
                .Implements<IElement>()
                .Implements<IXmlSerializable>()
                .Result);
        }

        [Fact]
        public void deserialize()
        {
            DivCustomElement obj = (
                "<custom.div type='example'>" +
                    "<div id='example' />" +
                    "<data>" +
                        "<value name='foo'>bar</value>" +
                    "</data>" +
                    "<validators>" +
                        "<validator display='error' for='control' type='SimpleWebServices.Web.UI.Html.Validation.StandardTextValidator, SimpleWebServices' />" +
                    "</validators>" +
                "</custom.div>").Deserialize<DivCustomElement>();

            Assert.Equal<Name>("example", obj.Element.Id);
            Assert.Equal<Type>(typeof(StandardTextValidator), Type.GetType(obj.ValidatorSettings[0].Type));
            Assert.Equal<string>("bar", obj.Data["foo"]);
        }

        [Fact]
        public void deserializeEmpty()
        {
            DivCustomElement actual = "<custom.div />".Deserialize<DivCustomElement>();

            Assert.Equal<string>("example", actual.Type);
            Assert.Empty(actual.Data);
            Assert.Empty(actual.ValidatorSettings);
        }

        [Fact]
        public void serializeFolded()
        {
            DivCustomElement obj = new DivCustomElement
            {
                Folded = true,
                Data =
                {
                    { "foo", "bar" }
                },
                Element = new Division
                {
                    Id = "example"
                },
                ValidatorSettings = 
                {
                    new ValidatorSettings
                    {
                        Type = typeof(StandardTextValidator).FullName
                    }
                }
            };

            XPathNavigator navigator = obj.Serialize().CreateNavigator();

            Assert.True((bool)navigator.Evaluate("1=count(/custom.div[@type='example'])"));
        }

        [Fact]
        public void serializeUnfolded()
        {
            DivCustomElement obj = new DivCustomElement
            {
                Folded = false,
                Data =
                {
                    { "foo", "bar" }
                },
                Element = new Division
                {
                    Id = "example"
                },
                ValidatorSettings = 
                {
                    new ValidatorSettings
                    {
                        Type = typeof(StandardTextValidator).FullName
                    }
                }
            };

            XPathNavigator navigator = obj.Serialize().CreateNavigator();

            Assert.True((bool)navigator.Evaluate("1=count(/custom.div/div[@id='example'])"));
            Assert.True((bool)navigator.Evaluate("1=count(/custom.div/validators/validator[@type='" + typeof(StandardTextValidator).FullName + "'])"));
            Assert.True((bool)navigator.Evaluate("1=count(/custom.div/data/value[@name='foo'][text()='bar'])"));
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new DivCustomElement() as CustomElement<Division>);
        }

        [Fact]
        public void prop_Class()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("Class"))
                .IsAutoProperty<Names>()
                .Result);
        }

        [Fact]
        public void prop_Data()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("Data"))
                .TypeIs<DataCollection>()
                .DefaultValueIsNotNull()
                .Result);
        }

        [Fact]
        public void prop_Element()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("Element"))
                .IsAutoProperty<IElement>()
                .Result);
        }

        [Fact]
        public void prop_Entity()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("Entity"))
                .TypeIs<string>()
                .DefaultValueIsNotNull()
                .Result);
        }

        [Fact]
        public void prop_Entity_get()
        {
            DivCustomElement obj = "<custom.div><div id='example' /></custom.div>".Deserialize<DivCustomElement>();

            string expected = "P2fKoTFFlknEeNr+pVYa6A==";
            string actual = MD5Hash.Compute(obj.Entity);

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void prop_Folded()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("Folded"))
                .IsAutoProperty<bool>()
                .Result);
        }

        [Fact]
        public void prop_Id()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("Id"))
                .IsAutoProperty<Name>()
                .Result);
        }

        [Fact]
        public void prop_Language()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("Language"))
                .IsAutoProperty<Language>()
                .Result);
        }

        [Fact]
        public void prop_Name()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("Name"))
                .TypeIs<string>()
                .DefaultValueIs("custom.div")
                .Result);
        }

        [Fact]
        public void prop_Title()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("Title"))
                .IsAutoProperty<string>()
                .Result);
        }

        [Fact]
        public void prop_Type()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("Type"))
                .TypeIs<string>()
                .DefaultValueIs("example")
                .Result);
        }

        [Fact]
        public void prop_ValidatorSettings()
        {
            Assert.True(new PropertyExpectations(typeof(DivCustomElement).GetProperty("ValidatorSettings"))
                .TypeIs<ValidatorSettingsCollection>()
                .DefaultValueIsNotNull()
                .Result);
        }

        [Fact]
        public void op_Deserialize_string_string_string()
        {
            Division actual = new DivCustomElement().Deserialize("custom.div", "example", "<custom.div type='example' />") as Division;

            Assert.IsAssignableFrom<StandardTextValidator>((actual["control"] as IControl).Validator);
        }

        [Fact]
        public void op_Deserialize_string_string_stringWithData()
        {
            string xml =
                "<custom.div type='example'>" +
                    "<data>" +
                        "<value name='foo'>bar</value>" +
                    "</data>" +
                "</custom.div>";

            Division actual = new DivCustomElement().Deserialize("custom.div", "example", xml) as Division;

            Assert.IsAssignableFrom<StandardTextValidator>((actual["control"] as IControl).Validator);
        }

        [Fact]
        public void op_Deserialize_stringNull_string_string()
        {
            Division actual = new DivCustomElement().Deserialize(null as string, "example", "<div id='example' />") as Division;

            Assert.Null(actual);
        }

        [Fact]
        public void op_Deserialize_stringEmpty_string_string()
        {
            Division actual = new DivCustomElement().Deserialize(string.Empty, "example", "<div id='example' />") as Division;

            Assert.Null(actual);
        }

        [Fact]
        public void op_Deserialize_string_stringNull_string()
        {
            Division actual = new DivCustomElement().Deserialize("custom.div", null as string, "<div id='example' />") as Division;

            Assert.Null(actual);
        }

        [Fact]
        public void op_Deserialize_string_stringEmpty_string()
        {
            Division actual = new DivCustomElement().Deserialize("custom.div", string.Empty, "<div id='example' />") as Division;

            Assert.Null(actual);
        }

        [Fact]
        public void op_Deserialize_string_string_stringNull()
        {
            Assert.Throws<ArgumentNullException>(() => new DivCustomElement().Deserialize("custom.div", "example", null as string));
        }

        [Fact]
        public void op_Deserialize_string_string_stringEmpty()
        {
            Assert.Throws<ArgumentOutOfRangeException>(() => new DivCustomElement().Deserialize("custom.div", "example", string.Empty));
        }

        [Fact]
        public void op_Deserialize_stringFold_string_string()
        {
            IElement actual = new DivCustomElement().Deserialize("fold:custom.div", "example", "<fold:custom.div type='example' />");

            Assert.IsAssignableFrom<DivCustomElement>(actual);
        }

        [Fact]
        public void op_Deserialize_stringFold_string_stringWithData()
        {
            string xml = 
                "<fold:custom.div type='example'>" +
                    "<data>" +
                        "<value name='foo'>bar</value>" +
                    "</data>" +
                "</fold:custom.div>";

            IElement actual = new DivCustomElement().Deserialize("fold:custom.div", "example", xml);

            Assert.IsAssignableFrom<DivCustomElement>(actual);

            Assert.True((bool)actual.Serialize().CreateNavigator().Evaluate("1=count(/custom.div[@type='example']/data/value[@name='foo'][text()='bar'])"));
        }

        [Fact]
        public void op_Render_whenFolded()
        {
            string expected = "<custom.div type=\"example\" />";

            DivCustomElement obj = new DivCustomElement
            {
                Folded = true
            };

            string actual = (obj as IElementSerializable).Render();

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_Render_whenUnfolded()
        {
            string expected = "<div></div>";

            DivCustomElement obj = new DivCustomElement
            {
                Folded = false
            };
            obj.Element = expected.Deserialize<Division>();

            string actual = (obj as IElementSerializable).Render();

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_ToElement()
        {
            IElement expected = new Division();

            DivCustomElement obj = new DivCustomElement();
            obj.Element = expected;

            IElement actual = (obj as IElementSerializable).ToElement();

            Assert.Same(expected, actual);
        }

        [Fact]
        public void op_ToString()
        {
            DivCustomElement obj = new DivCustomElement
            {
                Folded = true,
                Element = new Division
                {
                    Id = "example"
                },
                ValidatorSettings = 
                {
                    new ValidatorSettings
                    {
                        Type = typeof(StandardTextValidator).FullName
                    }
                }
            };

            Assert.Equal<string>(obj.Serialize().CreateNavigator().OuterXml, obj.ToString());
        }

        [Fact]
        public void op_GetSchema()
        {
            Assert.Throws<NotSupportedException>(() => (new DivCustomElement() as IXmlSerializable).GetSchema());
        }

        [Fact]
        public void op_ReadXml_XmlReaderNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new DivCustomElement() as IXmlSerializable).ReadXml(null as XmlReader));
        }

        [Fact]
        public void op_WriteXml_XmlWriterNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new DivCustomElement() as IXmlSerializable).WriteXml(null as XmlWriter));
        }
    }
}